package com.kexilo.core.common.filter;

import com.kexilo.core.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Pattern;

/**
 * SQL注入防护过滤器
 * 
 * @author Kexilo
 */
public class SqlInjectionFilter implements Filter {
    
    private static final Logger log = LoggerFactory.getLogger(SqlInjectionFilter.class);
    
    /**
     * SQL注入攻击模式
     */
    private static final Pattern[] SQL_INJECTION_PATTERNS = {
        Pattern.compile("('|(\\-\\-)|(;)|(\\|)|(\\*)|(%))", Pattern.CASE_INSENSITIVE),
        Pattern.compile("(exec|execute|insert|select|delete|update|count|drop|chr|mid|master|truncate|char|declare|sitename|net user|xp_cmdshell)", Pattern.CASE_INSENSITIVE),
        Pattern.compile("(net localgroup administrators|and|or|not|union|where|order|by|asc|desc)", Pattern.CASE_INSENSITIVE),
        Pattern.compile("(sleep|benchmark|load_file|outfile)", Pattern.CASE_INSENSITIVE),
        Pattern.compile("(\\bunion\\b.*(\\bselect\\b|\\bfrom\\b))", Pattern.CASE_INSENSITIVE),
        Pattern.compile("(\\bselect\\b.*(\\bfrom\\b|\\bwhere\\b))", Pattern.CASE_INSENSITIVE)
    };
    
    /**
     * 白名单参数（不进行SQL注入检查）
     */
    private static final String[] WHITE_LIST_PARAMS = {
        "password", "oldPassword", "newPassword", "confirmPassword"
    };
    
    @Override
    public void init(FilterConfig filterConfig) {
        log.info("SQL注入防护过滤器初始化完成");
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // 检查是否包含SQL注入攻击
        if (containsSqlInjection(httpRequest)) {
            log.warn("检测到SQL注入攻击: IP={}, URI={}", getClientIP(httpRequest), httpRequest.getRequestURI());
            httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            httpResponse.getWriter().write("{\"code\":400,\"msg\":\"请求参数包含非法字符\"}");
            return;
        }
        
        SqlInjectionHttpServletRequestWrapper wrappedRequest = new SqlInjectionHttpServletRequestWrapper(httpRequest);
        chain.doFilter(wrappedRequest, response);
    }
    
    @Override
    public void destroy() {
        log.info("SQL注入防护过滤器销毁");
    }
    
    /**
     * 检查请求是否包含SQL注入攻击
     */
    private boolean containsSqlInjection(HttpServletRequest request) {
        // 检查URL参数
        String queryString = request.getQueryString();
        if (StringUtils.isNotEmpty(queryString) && containsSqlKeywords(queryString)) {
            return true;
        }
        
        // 检查POST参数
        if (request.getParameterMap() != null) {
            for (String paramName : request.getParameterMap().keySet()) {
                if (isWhiteListParam(paramName)) {
                    continue;
                }
                
                String[] values = request.getParameterValues(paramName);
                if (values != null) {
                    for (String value : values) {
                        if (StringUtils.isNotEmpty(value) && containsSqlKeywords(value)) {
                            return true;
                        }
                    }
                }
            }
        }
        
        return false;
    }
    
    /**
     * 检查值是否包含SQL关键字
     */
    private boolean containsSqlKeywords(String value) {
        if (StringUtils.isEmpty(value)) {
            return false;
        }
        
        String lowerValue = value.toLowerCase();
        for (Pattern pattern : SQL_INJECTION_PATTERNS) {
            if (pattern.matcher(lowerValue).find()) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 检查是否为白名单参数
     */
    private boolean isWhiteListParam(String paramName) {
        if (StringUtils.isEmpty(paramName)) {
            return false;
        }
        
        for (String whiteParam : WHITE_LIST_PARAMS) {
            if (paramName.toLowerCase().contains(whiteParam.toLowerCase())) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 获取客户端IP
     */
    private String getClientIP(HttpServletRequest request) {
        String xip = request.getHeader("X-Real-IP");
        String xfor = request.getHeader("X-Forwarded-For");
        
        if (StringUtils.isNotEmpty(xfor) && !"unKnown".equalsIgnoreCase(xfor)) {
            int index = xfor.indexOf(",");
            if (index != -1) {
                return xfor.substring(0, index);
            } else {
                return xfor;
            }
        }
        
        xfor = xip;
        if (StringUtils.isNotEmpty(xfor) && !"unKnown".equalsIgnoreCase(xfor)) {
            return xfor;
        }
        
        if (StringUtils.isBlank(xfor) || "unknown".equalsIgnoreCase(xfor)) {
            xfor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xfor) || "unknown".equalsIgnoreCase(xfor)) {
            xfor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xfor) || "unknown".equalsIgnoreCase(xfor)) {
            xfor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(xfor) || "unknown".equalsIgnoreCase(xfor)) {
            xfor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(xfor) || "unknown".equalsIgnoreCase(xfor)) {
            xfor = request.getRemoteAddr();
        }
        
        return xfor;
    }
    
    /**
     * SQL注入请求包装器
     */
    private static class SqlInjectionHttpServletRequestWrapper extends HttpServletRequestWrapper {
        
        public SqlInjectionHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);
        }
        
        @Override
        public String[] getParameterValues(String parameter) {
            String[] values = super.getParameterValues(parameter);
            if (values == null) {
                return null;
            }
            
            int count = values.length;
            String[] encodedValues = new String[count];
            for (int i = 0; i < count; i++) {
                encodedValues[i] = cleanSqlKeywords(values[i]);
            }
            
            return encodedValues;
        }
        
        @Override
        public String getParameter(String parameter) {
            String value = super.getParameter(parameter);
            return cleanSqlKeywords(value);
        }
        
        /**
         * 清理SQL关键字
         */
        private String cleanSqlKeywords(String value) {
            if (StringUtils.isEmpty(value)) {
                return value;
            }
            
            // 简单的转义处理，实际项目中应该使用更严格的参数绑定
            return value.replaceAll("'", "''")
                       .replaceAll("\"", "\"\"")
                       .replaceAll(";", "；")
                       .replaceAll("--", "——");
        }
    }
}
